﻿namespace Baggr.Agent
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    using System.Linq;
    using FeedParser;
    using log4net;
    using Wintellect.Threading.AsyncProgModel;
    using System.Data.SqlClient;

    public sealed class PublishFeed
    {
        private readonly string _ConnectionString;
        private static readonly ILog Log = LogManager.GetLogger(typeof (PublishFeed));

        public PublishFeed(string connectionString)
        {
            if (connectionString == null)
                throw new ArgumentNullException("connectionString");
            _ConnectionString = connectionString;
        }

        public void Publish(
            Guid feedId,
            IFeed feed)
        {
            Log.InfoFormat("Begin: {0}", feedId);
            if (feed == null)
                throw new ArgumentNullException("feed");
            var logger = LogManager.GetLogger(Guid.NewGuid().ToString());
            using (var connection = new SqlConnection(_ConnectionString))
            {
                connection.Open();
                var command = CreateFilterCommand(connection, feedId, feed.Items.Select(curr => curr.LocalId));
                SqlDataReader reader;

                try
                {
                    reader = command.ExecuteReader();
                }
                catch (SqlException e)
                {
                    logger.Info("Error: " + feedId, e);
                    return;
                }

                var newFeedIds = new List<Guid>();
                while (reader.Read())
                {
                    var id = reader.GetGuid(0);
                    newFeedIds.Add(id);
                }
                reader.Dispose();
                foreach (var feedItem in feed.Items)
                {
                    if (newFeedIds.Contains(feedItem.LocalId))
                    {
                        var postFeedItemCommand = CreatePostCommand(connection, feedId, feedItem);
                        postFeedItemCommand.ExecuteNonQuery();
                    }
                }

                using (var markFeedUpdatedCommand = connection.CreateCommand())
                {
                    markFeedUpdatedCommand.CommandText = "MarkFeedUpdated";
                    markFeedUpdatedCommand.CommandType = CommandType.StoredProcedure;
                    markFeedUpdatedCommand.Parameters.AddWithValue("@FeedId", feedId);
                    markFeedUpdatedCommand.ExecuteNonQuery();
                }
            }
            Log.InfoFormat("End: {0}", feedId);
        }

        /// <summary>
        /// Create command filtering exists feed items.
        /// </summary>
        /// <param name="connection">Opened connection</param>
        /// <param name="feedId">Id of feed.</param>
        /// <param name="feedItemsIds">Collection of read feed items</param>
        /// <returns>Created command</returns>
        private static SqlCommand CreateFilterCommand(
            SqlConnection connection,
            Guid feedId,
            IEnumerable<Guid> feedItemsIds)
        {
            if (feedItemsIds.Count() == 0)
                return null;
            var command = connection.CreateCommand();
            command.CommandText = "FilterExists";
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.AddWithValue("@FeedId", feedId);
            command.Parameters.AddWithValue("@FeedItems", feedItemsIds.Serialize());
            return command;
        }

        /// <summary>
        /// Create command for publish feed item.
        /// </summary>
        /// <param name="connection">Connection for publication</param>
        /// <param name="feedId">Guid of feed</param>
        /// <param name="feedItem">Publicationing Feed item</param>
        /// <returns>Created command</returns>
        private static SqlCommand CreatePostCommand(
            SqlConnection connection,
            Guid feedId,
            IFeedItem feedItem)
        {
            var command = connection.CreateCommand();
            command.CommandText = "PostFeedItem";
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.AddWithValue("@FeedId", feedId);
            command.Parameters.AddWithValue("@Id", Guid.NewGuid());
            command.Parameters.AddWithValue("@Date", feedItem.PublicationDate);
            command.Parameters.AddWithValue("@Title", feedItem.Title);
            command.Parameters.AddWithValue("@Description", feedItem.Description);
            command.Parameters.AddWithValue("@Link", feedItem.Link.ToString());
            command.Parameters.AddWithValue("@LocalId", feedItem.LocalId);
            command.Parameters.AddWithValue("@Author", feedItem.Author);
            return command;
        }
    }
}
